package business.dados;

import java.util.ArrayList;
import java.util.List;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.classic.Session;
import org.hibernate.criterion.Restrictions;
import business.sistema.IObjetoSistema;
import business.sistema.ObjetoSistema;
import business.sistema.Properties;
import business.util.Menssagens;

public class Persistente 
{	
	private Session session = null;
	private Transaction transaction = null;
	private static Persistente objBanco = null;
	private SessionFactory sessionFactory;
	
	private Persistente() throws Exception
	{		
		Configuration config = new Configuration();
		
		/* Propriedades */
		config.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
		config.setProperty("hibernate.connection.driver_class", "org.postgresql.Driver");
//		config.setProperty("hibernate.connection.url", Properties.URL());
		config.setProperty("hibernate.connection.url", "jdbc:postgresql://localhost:5432/erpelenge");
		config.setProperty("hibernate.connection.username", "postgres");
		config.setProperty("hibernate.connection.password", "pos73dda");
		config.setProperty("hibernate.transaction.auto_close_session", "false");
		config.setProperty("hibernate.show_sql", "true");
		config.setProperty("hibernate.format_sql", "true");
		config.setProperty("hibernate.use_sql_comments", "true");
		config.setProperty("hibernate.connection.autocommit", "true");
		
		/* Mapeamentos */		
		for(String mapeamento : Properties.MAPEAMENTO())
		{
			config.addResource(mapeamento);
		}
		
//		sessionFactory = config.configure().buildSessionFactory();
		
		sessionFactory = config.buildSessionFactory();
		
		session = sessionFactory.openSession();
	}
	
	public static Persistente getInstance() throws Exception
	{
		if (objBanco == null)
		{
			objBanco = new Persistente();
		}
		
		return objBanco;
	}
	
	/**
	 * Método responsável por salvar objetos individuais no banco
	 * 
	 * @param obj Objeto que será salvo no banco
	 */
	public void save(IObjetoSistema objeto) throws Exception
	{		
		try
		{
			Session session = getSession();
			transaction = session.beginTransaction();
			session.saveOrUpdate(objeto);
			transaction.commit();
		}
		catch(Exception he)
		{
			if (transaction != null)
			{
				transaction.rollback();
			}
			
			throw new Exception(Menssagens.errSalvar, he);
		}
	}
	
	/**
	 * Método responsável por salvar uma lista de objetos no banco
	 * 
	 * @param lista : ArrayList<IObjetoSistema>
	 * @param classe : Class - A classe de objetos que serão salvos no banco
	 * @throws Exception
	 */
	public void save(ArrayList<IObjetoSistema> lista) throws Exception
	{		
		try
		{
			Session session = getSession();
			transaction = session.beginTransaction();
			
			for(IObjetoSistema objeto : lista)
			{
				session.saveOrUpdate(objeto);
			}
			
			transaction.commit();
		}
		catch(Exception he)
		{
			if (transaction != null)
			{
				transaction.rollback();
			}
			
			throw new Exception(Menssagens.errSalvar, he);
		}
	}
	
	/**
	 * Método responsável por remover objeto do banco
	 * 
	 * @param object Objeto que será removido do banco
	 */
	public void Remove(Object object) throws Exception
	{
		try
		{
			Session session = getSession();
			transaction = session.beginTransaction();
			session.delete(object);
			transaction.commit();
		}
		catch(Exception he)
		{
			if (transaction != null) 
			{
				transaction.rollback();
			}
			
			throw new Exception("Não foi possível excluir!", he);
		}
	}

	/**
	 * Método responsável por recuperar um objeto do banco
	 * 
	 * @param obj Objeto que será recuperado
	 * @param id identificador do objeto
	 * @return Objeto com os dados pertencentes ao objeto
	 */
	public IObjetoSistema getObject(IObjetoSistema objeto, int identificador) throws Exception
	{
		try
		{
			Session session = getSession();
			objeto = (IObjetoSistema)session.get(objeto.getClass(), new Integer(identificador));
		}
		catch(Exception he)
		{
			throw new Exception("Não foi possível obter objeto!", he);
		}
		
		return objeto;
	}
	
	
	/**
	 * Método responsável por recuperar uma coleção de objetos do banco.
	 * 
	 * @param classe : Class - Classe da coleção de objetos.
	 * @param sql : String - critério de busca.
	 * @return lista : List<IObjetoSistema> - Lista com os objetos.
	 */
	@SuppressWarnings("unchecked")
	public List<IObjetoSistema> getCollection(Class<?> classe, String sql) throws Exception
	{		
		List<IObjetoSistema> lista = new ArrayList<IObjetoSistema>();
		
		try
		{
			Session session = getSession();
			Criteria criterio = session.createCriteria(classe);
			criterio.add(Restrictions.sqlRestriction(sql));
			lista = criterio.list();
		}
		catch(Exception e)
		{
			throw new Exception(Menssagens.naoFoiPossivelObterLista, e);
		}
		
		return lista;
	}
	
	/**
	 * Classe responsável por retornar a coleção de objetos por consulta em HQL.
	 * 
	 * @param classe
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public List<IObjetoSistema> getCollection(Class<?> classe, List<String[]> outrasTabelas, String restricoes) throws Exception
	{		
		List<IObjetoSistema> lista = new ArrayList<IObjetoSistema>();
		
		try
		{
			Session session = getSession();
			Criteria criterio = session.createCriteria(classe);
			
			for(String[] tabela : outrasTabelas)
			{
				if(tabela.length == 2)
				{
					criterio.createCriteria(tabela[0], tabela[1]);
				}
			}
			
			criterio.add(Restrictions.sqlRestriction(restricoes));
			lista = criterio.list();
		}
		catch(Exception e)
		{
			throw new Exception(Menssagens.naoFoiPossivelObterLista, e);
		}
		
		return lista;
	}
	
	/**
	 * Método responsável por recuperar uma coleção de objetos do banco.
	 * 
	 * @param sql : String - critério de busca.
	 * @return lista : List<IObjetoSistema> - Lista com os objetos.
	 */
	@SuppressWarnings("unchecked")
	public List<IObjetoSistema> getCollection(String sql) throws Exception
	{		
		List<IObjetoSistema> lista = new ArrayList<IObjetoSistema>();

		try
		{			
			Session session = getSession();
			Query query = session.createQuery(sql);
			lista = query.list();
		}
		catch(Exception ex)
		{
			throw new Exception(Menssagens.naoFoiPossivelObterLista, ex);
		}
		
		return lista;
	}
	
	/**
	 * 
	 */
	public void desfazerModificacao(ObjetoSistema objeto) throws Exception
	{
		getSession().evict(objeto);
	}
	
	private Session getSession()
	{
		if(!session.isOpen())
		{
			session = sessionFactory.openSession();
		}
		
		return session;
	}
}